package org.nxl.ExceptionDetector;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class Task implements Runnable {

	public String getApp() {
		return app;
	}

	public void setApp(String app) {
		this.app = app;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getSession() {
		return session;
	}

	public void setSession(String session) {
		this.session = session;
	}

	public String getConsumerId() {
		return consumerId;
	}

	public void setConsumerId(String consumerId) {
		this.consumerId = consumerId;
	}

	private String app;
	private String url;
	private String session;
	private String cmd;
	private String consumerId;
	private String exception;
	public String getException() {
		return exception;
	}

	public void setException(String exception) {
		this.exception = exception;
	}

	private BlockingQueue<Map> queue;
	volatile private int status = 0; //status = -30 not retry; status = -20 will retry in 40s; status = 0 normal start ;status=1 session inited; status=2, unsafe set; status=3, cmd success, may pull results
	
	public JSONObject executeCmd(Map cmd) {
		CloseableHttpClient httpClient = HttpClientBuilder.create().build();
		HttpPost httpPost = new HttpPost(getUrl());
		JSONObject cmdJson = new JSONObject(cmd);
		StringEntity entity = new StringEntity(cmdJson.toJSONString(), "UTF-8");
		httpPost.setEntity(entity);
		httpPost.setHeader("Content-Type", "application/json;charset=utf8");
		// 响应模型
		CloseableHttpResponse response = null;
		try {
			response = httpClient.execute(httpPost);
			if(response == null || response.getStatusLine() == null) {
				System.out.println(app + "@" + url + " http connection unknown error, will retry in 40s");
				status = -20;
				return null;
			}
			if (response.getStatusLine().getStatusCode() != 200) {
				System.out.println(app + "@" + url + " http status is " + response.getStatusLine().getStatusCode() + " will retry in 40s");
				status = -20;
				return null;
			}
			HttpEntity responseEntity = response.getEntity();
			if (responseEntity != null) {
				String ret = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
				JSONObject ret_json = JSONObject.parseObject(ret);
				return ret_json;
			} else {
				System.out.println(app + "@" + url + " http result is empty, will retry in 40s");
				status = -20;
				return null;
			}
		} catch (Exception e) {
			if(e instanceof ClientProtocolException) {
				System.out.println(app + "@" + url + " port is not http, will not retry, please check");
				status = -30;
			}
			if(e instanceof HttpHostConnectException || e instanceof IOException) {
				System.out.println(app + "@" + url + " connection refused, will retry in 40s");
				status = -20;
			}
			
		}
		return null;
	}

	public void initSession() {
		Map map = new HashMap();
		map.put("action", "init_session");
		JSONObject result = executeCmd(map);
		if(result != null) {
			String sessionId = result.getString("sessionId");
			String consumerId = result.getString("consumerId");
			if(sessionId != null && consumerId != null) {
				setSession(sessionId);
				setConsumerId(consumerId);
				this.status++;
				System.out.println(app + "@" + url + " init session succeed, sessionId is "+ sessionId + ", consumerId is " + consumerId);
			} else {
				System.out.println(app + "@" + url + " could not fetch sessionid and consumerid, will retry in 40s");
				this.status = -20;
			}
		} 
	}

	//{"action":"async_exec","sessionId":"9eb4ffd4-3a87-496c-b229-655717f83bd5","consumerId":"42e8a17fed9b440a93055fc219bb868e_2","command":"options unsafe true"}
	public void setUnsafe() {
		Map req = new HashMap();
		req.put("action", "async_exec");
		req.put("sessionId",session);
		req.put("consumerId", consumerId);
		req.put("command", "options unsafe true");
		JSONObject ret = executeCmd(req);
		if(ret != null) {
			String state = ret.getString("state");
			if(state != null) {
				System.out.println(app + "@" + url + " set unsafe true succeed");
				status++;
			} else {
				System.out.println(app + "@" + url + " set unsafe true failed, will reinit session");
				status = 0;
			}
		}
	}

	//{"action":"async_exec","sessionId":"9eb4ffd4-3a87-496c-b229-655717f83bd5","consumerId":"42e8a17fed9b440a93055fc219bb868e_2","command":"watch java.lang.NullPointerException <init> target.getStackTrace()[0].toString()"}
	public void executeWatch() {
		Map req = new HashMap();
		req.put("action", "async_exec");
		req.put("sessionId", this.session);
		req.put("consumerId", consumerId);
		req.put("command", "watch " + exception + " <init> target.getStackTrace()[0].toString()");
		
		JSONObject ret = executeCmd(req);
		if(ret != null) {
			String state = ret.getString("state");
			if("SCHEDULED".equals(state)) {
					status++;
					System.out.println(app + "@" + url + " issue  " + "watch " + exception + " <init> target.getStackTrace()[0].toString() succeed");
			} else {
				status = 1;
			}
		}
	}
	public void pullresults() {
		Map req = new HashMap();
		req.put("action", "pull_results");
		req.put("sessionId", this.session);
		req.put("consumerId", consumerId);
		JSONObject ret_json = executeCmd(req);
		if(ret_json != null) {	
			String state = ret_json.getString("state");
			if(! "SUCCEEDED".equals(state)) {
				status = 2;
				return;
			}
			JSONArray array = ret_json.getJSONObject("body").getJSONArray("results");
			System.out.println(app + "@" + url + " results:" + ret_json.toJSONString());	
			if(status >= 53 && array.isEmpty()) {
				System.out.println(app + "@" + url + " , pull more than 50 times, empty result, re execute watch cmd");	
				status = 2;
				return;
			}
			status ++;
			for(Object o : array) {
				JSONObject oo = (JSONObject)o;
				String type = oo.getString("type");
				if("watch".equals(type)) {
					//System.out.println("java.lang.NullPointerExceptin:" + oo.getString("value"));
					Map map = new HashMap<String,String>();
					map.put("app", app);
					map.put("exception", exception);
					map.put("code", oo.getString("value"));
					queue.add(map);
					//System.out.println(queue.size());
				}
			}
		}

	}

	public Task() {

	}

	public void run() {
		if(status == 0) {
			initSession();
		} else if(status == 1) {
			setUnsafe();
		} else if(status == 2) {
			executeWatch();
		} else if(status >= 3) {
			pullresults();
		} else if(status > -30 && status < 0) {
			status++;
		}
	}
	public BlockingQueue<Map> getQueue() {
		return queue;
	}

	public void setQueue(BlockingQueue<Map> queue) {
		this.queue = queue;
	}
	public static void main(String[] args) {
		Task task = new Task();
		task.setApp("test");
		task.setException("java.lang.NullPointerException");
		task.setUrl("http://127.0.0.1:33306/api");
		task.initSession();
	}
}
